<template>
  <el-upload
      ref="uploadRef"
      class="upload"
      action="#"
      :list-type="props.listType"
      :multiple="props.limit > 1"
      :show-file-list="props.showFileList"
      :drag="props.drag"
      :limit="props.limit"
      :auto-upload="props.autoUpload"
      :on-exceed="handleExceed"
      :http-request="handleUpload"
      :on-remove="handleRemove"
      :before-upload="beforeUpload"
      v-model:file-list="fileList"
      :disabled="submitLoading || fileList.length >= props.limit"
  >
    <el-icon :size="props.iconSize" :color="props.iconColor">
      <upload-filled/>
    </el-icon>
    <div class="el-upload__text">
      拖拽上传 或 <em>点击上传</em>
    </div>
    <template #tip>
      <div class="el-upload__tip">
        请上传小于 <span>{{ props.fileSize }}MB</span> 的文件，最多上传 <span>{{ props.limit }}个</span> 文件{{ props.accept.length > 0 ? "，允许上传的格式：" + props.accept.join(",") : null }}
        <el-button
            v-show="!autoUpload"
            class="submit-button"
            type="success"
            @click="submitUpload"
            :loading="submitLoading"
            :disabled="readyNumber === 0"
        >
          上传
        </el-button>
      </div>
      <div class="mg-t-10" v-show="fileList.length > 0">
        <Progress :percentage="percentage.value"/>
      </div>
    </template>
    <template #file="{ file }">
      <div class="uploadFile">
        <div class="upload-info">
          <div v-if="isImg(file.raw.type)">
            <Image :size="60" :src="file.url"/>
          </div>
          <div>
            <div class="uploadFile-name">{{ file.name }}</div>
            <div>文件大小: {{ (file.raw.size / 1024 / 1024).toFixed(2) }}MB</div>
          </div>
        </div>
        <div>
          <el-icon color="#67C23A" :size="25" v-show="file.status === 'success'">
            <SuccessFilled/>
          </el-icon>
          <el-icon color="#F56C6C" :size="25" v-show="file.status === 'fail'">
            <CircleCloseFilled/>
          </el-icon>
          <el-icon color="#409eff" :size="25" v-show="file.status === 'uploading'" class="is-loading">
            <Loading/>
          </el-icon>
          &nbsp;
          <el-button type="danger" :icon="Delete" @click="handleDel(file)" circle />
        </div>
      </div>
    </template>
  </el-upload>
</template>

<script setup lang="ts">
import {CircleCloseFilled, Delete, Loading, SuccessFilled, UploadFilled} from "@element-plus/icons-vue";
import {
  UploadFile,
  UploadInstance,
  UploadRawFile,
  UploadRequestOptions,
  UploadStatus,
  UploadUserFile,
} from "element-plus";
import {Response} from "@/entity/response";

const props = defineProps({
  // 是否显示已上传文件列表
  showFileList: {
    type: Boolean,
    default: true
  },
  // 是否支持拖拽上传
  drag: {
    type: Boolean,
    default: true
  },
  // 文件上传数量限制
  limit: {
    type: Number,
    default: 1
  },
  // 图标颜色
  iconColor: {
    type: String,
    default: "#409eff"
  },
  // 图标大小
  iconSize: {
    type: Number,
    default: 70
  },
  // 自动上传
  autoUpload: {
    type: Boolean,
    default: false
  },
  // 上传文件大小限制 MB
  fileSize: {
    type: Number,
    default: 10
  },
  // 文件列表的类型
  listType: {
    type: String,
    default: "picture"
  },
  // 允许上传的文件类型
  accept: {
    type: Array,
    default: () => []
  },
});
const emits = defineEmits(["uploadSuccess"]);
const {msgSuccess, msgWarning} = useUtils().ModalUtils;
const {ossApi} = useApi();
const uploadRef = ref<UploadInstance>();
const submitLoading = ref(false);
const fileList = ref<UploadUserFile[]>([]);
const readyNumber = ref(0);
const successNumber = ref(0);
const percentage = reactive({
  total: 0,
  add: 0,
  value: 0,
});
const imgType = ["image/jpeg", "image/png", "image/gif", "image/bmp", "image/webp", "image/svg+xml", "image/x-icon"];

watch(
    () => fileList,
    () => {
      // 获取 待上传 和 上传失败 的文件数量
      readyNumber.value = fileList.value.filter(item => item.status === "ready" || item.status === "fail").length;
      // 获取 上传成功 的文件数量
      successNumber.value = fileList.value.filter(item => item.status === "success").length;
    },
    {deep: true}
);

const cleanFileList = () => {
  fileList.value = [];
  percentage.value = 0;
}
const handleDel = (file: UploadFile) => {
  console.log(file);
  const index = fileList.value.findIndex(item => item.uid === file.uid);
  fileList.value.splice(index, 1);
  msgSuccess("移除成功");
}
const isImg = (type: string) => {
  return imgType.includes(type);
}
const handleExceed = () => {
  msgWarning("上传文件数量不能超过" + props.limit + "个");
  submitLoading.value = false;
};
const handleRemove = () => {
  if (fileList.value.length === 0) {
    percentage.value = 0;
  }
}
const beforeUpload = (rawFile: UploadRawFile) => {
  submitLoading.value = true;
  const size = rawFile.size / 1024 / 1024; // MB
  if (size > props.fileSize) {
    msgWarning("上传文件大小不能超过" + props.fileSize + "MB");
    submitLoading.value = false;
    return false;
  }
  const type = rawFile.type;
  if (props.accept.length > 0 && !props.accept.includes(type)) {
    msgWarning("上传文件类型不正确");
    submitLoading.value = false;
    return false;
  }
}
const handelStatusChange = () => {
  // 状态改变时执行
  if (fileList.value.every(item => item.status === "success")) {
    submitLoading.value = false;
    msgSuccess("上传成功");
  }
}
const handleUpload = (options: UploadRequestOptions) => {
  const file: any = options.file;
  let formData = new FormData();
  formData.append("file", file);
  updateFileStatus(file.uid, "uploading");
  ossApi.uploadFile(formData).then((res: any) => {
    let {code, data}: Response = res;
    let {fileName, ossId, url} = data;
    if (code === 200) {
      updateFileStatus(file.uid, "success");
      // 判断当前上传的文件是否为最后一个
      if (fileList.value.length === successNumber.value) {
        percentage.value = 100;
      } else {
        percentage.value += percentage.add;
      }
      emits("uploadSuccess", {fileName, ossId, url});
    }
  }).catch((err: any) => {
    updateFileStatus(file.uid, "fail");
    console.error("上传时出现异常：", err);
  });
};
const updateFileStatus = (uid: number, status: UploadStatus) => {
  const index = fileList.value.findIndex(item => item.uid === uid);
  fileList.value[index].status = status;
  handelStatusChange();
}
const submitUpload = () => {
  // 计算上传进度
  percentage.total = readyNumber.value;
  percentage.add = Math.trunc(100 / percentage.total);
  percentage.value = 0;
  uploadRef.value!.submit();
}
defineExpose({
  cleanFileList
});
</script>

<style scoped lang="scss">
.upload {
  width: 100%;
  height: 100%;

  .el-upload__tip {
    display: flex;
    align-items: center;

    span {
      color: red;
    }

    .submit-button {
      margin-left: 20px;
    }
  }

  .uploadFile {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;

    .upload-info {
      display: flex;
      align-items: center;

      &>div:first-child {
        margin-right: 5px;
        height: 60px;
      }

      .uploadFile-name {
        font-weight: bold;
      }
    }

    &>div:last-child {
      display: flex;
      align-items: center;
    }
  }
}
</style>